﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using Adalbertus.BudgetPlanner.Services;
using Caliburn.Micro;

namespace Adalbertus.BudgetPlanner.ViewModels.ExternalSources
{
    public class ExternalSourcesViewModel : Screen
    {
        private ISavingService _savingService;
        private IIncomeService _incomeService;

        public BindableCollection<SavingVM> Savings { get; private set; }
        public BindableCollection<IncomeVM> Incomes { get; private set; }

        public ExternalSourcesViewModel(ISavingService savingService, IIncomeService incomeService)
        {
            Contract.Requires<ArgumentNullException>(incomeService != null);
            Contract.Requires<ArgumentNullException>(savingService != null);

            _savingService = savingService;
            _incomeService = incomeService;

            Savings = new BindableCollection<SavingVM>();
            Incomes = new BindableCollection<IncomeVM>();
        }

        protected override void OnActivate()
        {
            base.OnActivate();
            
            IEnumerable<SavingVM> savings = _savingService.GetAll().Select(x => new SavingVM(x)).ToList();
            Savings.Clear();
            Savings.AddRange(savings);
            foreach (SavingVM saving in Savings)
            {
                saving.PropertyChanged += SavingPropertyChanged;
            }

            IEnumerable<IncomeVM> incomes = _incomeService.GetAll().Select(x => new IncomeVM(x)).ToList();
            Incomes.Clear();
            Incomes.AddRange(incomes);
            foreach (IncomeVM income in Incomes)
            {
                income.PropertyChanged += IncomePropertyChanged;
            }
        }

        public void AddSaving()
        {
            string savingDefaultName = CreateUniqueName("Oszczędności", Savings.Select(x => x.WrappedSaving.Name).ToList());
            SavingVM newSaving = new SavingVM(_savingService.Create(savingDefaultName));
            newSaving.PropertyChanged += SavingPropertyChanged;
            Savings.Add(newSaving);
        }

        public void AddSavingEntry(SavingVM saving)
        {
            Contract.Requires<ArgumentNullException>(saving != null);
            Adalbertus.BudgetPlanner.Domain.SavingEntry createdSavingEntry =
                _savingService.CreateEntry(saving.WrappedSaving, 0, DateTime.Now);
            saving.AddEntry(createdSavingEntry);
        }

        public void RemoveSaving(SavingVM saving)
        {
            Contract.Requires<ArgumentNullException>(saving != null);
            Savings.Remove(saving);
            _savingService.Remove(saving.WrappedSaving);
        }
        
        public void RemoveSavingEntry(SavingEntryVM savingEntry)
        {
            Contract.Requires<ArgumentNullException>(savingEntry != null);
            savingEntry.Parent.RemoveEntry(savingEntry);
            _savingService.RemoveEntry(savingEntry.WrappedSavingEntry);
        }

        public void AddIncome()
        {
            string incomeDefaultName = CreateUniqueName("Dochód", Incomes.Select(x => x.WrappedIncome.Name).ToList());
            IncomeVM newIncome = new IncomeVM(_incomeService.Create(incomeDefaultName));
            Incomes.Add(newIncome);
        }

        public void RemoveIncome(IncomeVM income)
        {
            Contract.Requires<ArgumentNullException>(income != null);
            Incomes.Remove(income);
            _incomeService.Remove(income.WrappedIncome);
        }

        private string CreateUniqueName(string prefix, List<string> names)
        {
            string uniqueNameFormat = "{0} {1}";
            string uniqueName = string.Format(uniqueNameFormat, prefix, names.Count + 1);
            int counter = 2;
            while (names.Any(x => x.Equals(uniqueName)))
            {
                uniqueName = string.Format(uniqueNameFormat, prefix, names.Count + counter++);
            }

            return uniqueName;
        }

        private void SavingPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            SavingVM saving = sender as SavingVM;
            switch (e.PropertyName)
            {
                case "Name":
                case "Entries":
                    _savingService.Save(saving.WrappedSaving);
                    break;
            }
        }

        private void IncomePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            IncomeVM income = sender as IncomeVM;
            switch (e.PropertyName)
            {
                case "Name":
                    _incomeService.Save(income.WrappedIncome);
                    break;
            }
        }
    }
}
